package simpledb.optimizer;

import simpledb.common.Database;
import simpledb.common.Type;
import simpledb.execution.Predicate;
import simpledb.execution.SeqScan;
import simpledb.storage.*;
import simpledb.transaction.TransactionId;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * TableStats 表示查询中基础表的统计信息（例如直方图）。
 *
 * 该类在 lab1 和 lab2 中不是必需的，主要用于查询优化器进行代价估计和选择率估算。
 */
public class TableStats {

    // 存储每个表名对应的 TableStats 实例，线程安全
    private static final ConcurrentMap<String, TableStats> statsMap      = new ConcurrentHashMap<>();

    // 每页的 I/O 成本常量，用于代价估算
    static final int                                       IOCOSTPERPAGE = 1000;

    /**
     * 获取指定表名的 TableStats 对象
     */
    public static TableStats getTableStats(String tablename) {
        return statsMap.get(tablename);
    }

    /**
     * 设置指定表名的 TableStats 对象
     */
    public static void setTableStats(String tablename, TableStats stats) {
        statsMap.put(tablename, stats);
    }

    /**
     * 替换整个 statsMap，用于测试或重置统计信息
     */
    public static void setStatsMap(Map<String, TableStats> s) {
        try {
            java.lang.reflect.Field statsMapF = TableStats.class.getDeclaredField("statsMap");
            statsMapF.setAccessible(true);
            statsMapF.set(null, s);
        } catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException | SecurityException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取当前所有表的统计信息映射
     */
    public static Map<String, TableStats> getStatsMap() {
        return statsMap;
    }

    /**
     * 静态方法：为数据库中的每张表计算统计信息，并存储到 statsMap 中
     */
    public static void computeStatistics() {
        Iterator<Integer> tableIt = Database.getCatalog().tableIdIterator();

        System.out.println("Computing table stats.");
        while (tableIt.hasNext()) {
            int tableid = tableIt.next();
            TableStats s = new TableStats(tableid, IOCOSTPERPAGE);
            setTableStats(Database.getCatalog().getTableName(tableid), s);
        }
        System.out.println("Done.");
    }

    /**
     * 直方图使用的桶数量，默认值为100，可以增加但不能少于100
     */
    private static final int              NUM_HIST_BINS = 100;

    // 每个字段 ID 对应的直方图（可能是 IntHistogram 或 StringHistogram）
    private final Map<Integer, Histogram> histogramMap;

    private int                           totalTuples;        // 总元组数
    private int                           totalPages;         // 总页数
    private int                           tableId;            // 表ID
    private int                           ioCostPerPage;      // 每页IO成本
    private TupleDesc                     td;                 // 表的元组描述

    /**
     * 创建一个新的 TableStats 对象，用于跟踪表中每一列的统计信息
     *
     * @param tableid 要统计的表ID
     * @param ioCostPerPage 每页IO的成本
     */
    public TableStats(int tableid, int ioCostPerPage) {
        this.ioCostPerPage = ioCostPerPage;
        this.tableId = tableid;
        this.histogramMap = new HashMap<>();
        final HeapFile table = (HeapFile) Database.getCatalog().getDatabaseFile(tableid);
        this.totalPages = table.numPages(); // 获取总页数
        this.td = table.getTupleDesc(); // 获取元组结构

        // 获取每个字段的所有值
        final Map<Integer, ArrayList> fieldValues = fetchFieldValues(tableId);

        // 为每个字段构建直方图
        for (final int fieldId : fieldValues.keySet()) {
            if (td.getFieldType(fieldId) == Type.INT_TYPE) {
                final List<Integer> values = (ArrayList<Integer>) fieldValues.get(fieldId);
                final int minVal = Collections.min(values);
                final int maxVal = Collections.max(values);
                final IntHistogram histogram = new IntHistogram(NUM_HIST_BINS, minVal, maxVal);
                for (final Integer v : values) {
                    histogram.addValue(v);
                }
                this.histogramMap.put(fieldId, histogram);
            } else {
                final List<String> values = (ArrayList<String>) fieldValues.get(fieldId);
                final StringHistogram histogram = new StringHistogram(NUM_HIST_BINS);
                for (final String v : values) {
                    histogram.addValue(v);
                }
                this.histogramMap.put(fieldId, histogram);
            }
        }
    }

    /**
     * 使用顺序扫描获取表中每个字段的所有值
     */
    private Map<Integer, ArrayList> fetchFieldValues(final int tableId) {
        final Map<Integer, ArrayList> fieldValueMap = new HashMap<>();
        for (int i = 0; i < td.numFields(); i++) {
            if (td.getFieldType(i) == Type.INT_TYPE) {
                fieldValueMap.put(i, new ArrayList<Integer>());
            } else {
                fieldValueMap.put(i, new ArrayList<String>());
            }
        }

        final SeqScan seqScan = new SeqScan(new TransactionId(), tableId);
        try {
            seqScan.open();
            while (seqScan.hasNext()) {
                this.totalTuples++;
                final Tuple next = seqScan.next();
                for (int i = 0; i < td.numFields(); i++) {
                    final Field field = next.getField(i);
                    switch (field.getType()) {
                        case INT_TYPE: {
                            final int value = ((IntField) field).getValue();
                            fieldValueMap.get(i).add(value);
                            break;
                        }
                        case STRING_TYPE: {
                            final String value = ((StringField) field).getValue();
                            if (!Objects.equals(value, "")) {
                                fieldValueMap.get(i).add(value);
                            }
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fieldValueMap;
    }

    /**
     * 估计顺序扫描表的成本。假设每次读取一页的成本是固定的，不考虑缓存。
     *
     * @return 扫描该表的估计成本
     */
    public double estimateScanCost() {
        return this.ioCostPerPage * this.totalPages;
    }

    /**
     * 给定一个谓词的选择率，返回应用该谓词后表的元组数量估计值
     *
     * @param selectivityFactor 谓词的选择率（0.0 ~ 1.0）
     * @return 应用谓词后的元组数量估计值
     */
    public int estimateTableCardinality(double selectivityFactor) {
        return (int) (this.totalTuples * selectivityFactor);
    }

    /**
     * 返回某个字段在特定操作符下的平均选择率
     *
     * @param field 字段索引
     * @param op 查询操作符
     * @return 平均选择率
     */
    public double avgSelectivity(int field, Predicate.Op op) {
        return 1.0; // 默认实现，可扩展
    }

    /**
     * 估计某字段上谓词 "field op constant" 的选择率
     *
     * @param field 字段索引
     * @param op 操作符
     * @param constant 谓词比较值
     * @return 估计的选择率
     */
    public double estimateSelectivity(int field, Predicate.Op op, Field constant) {
        if (this.histogramMap.containsKey(field)) {
            switch (this.td.getFieldType(field)) {
                case INT_TYPE: {
                    final IntHistogram histogram = (IntHistogram) this.histogramMap.get(field);
                    return histogram.estimateSelectivity(op, ((IntField) constant).getValue());
                }
                case STRING_TYPE: {
                    final StringHistogram histogram = (StringHistogram) this.histogramMap.get(field);
                    return histogram.estimateSelectivity(op, ((StringField) constant).getValue());
                }
            }
        }
        return 0.0;
    }

    /**
     * 返回该表的总元组数量
     */
    public int totalTuples() {
        return this.totalTuples;
    }
}
